package org.groovymud.shell.delegates;

import org.apache.log4j.Logger;
import org.groovymud.object.alive.Player;
import org.groovymud.shell.Shell;
import org.groovymud.shell.SplashLoader;
import org.groovymud.shell.io.TerminalIO;
import org.groovymud.shell.security.callbacks.SelfHandlingCallback;
import org.groovymud.shell.telnetd.LoginShell;

import javax.security.auth.callback.*;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static java.util.Collections.synchronizedMap;
import static net.wimpi.telnetd.io.BasicTerminalIO.BLACK;

public class LoginDelegate implements CallbackHandler {
    private final static Logger logger = Logger.getLogger(LoginShell.class);

    private final SplashLoader splashLoader;
    private final int loginAttempts;

    private TerminalIO terminalIO;


    private Map<String, Object> callbackData = synchronizedMap(new HashMap<String, Object>());

    public LoginDelegate(SplashLoader splashLoader, int loginAttempts) {
        this.splashLoader = splashLoader;
        this.loginAttempts = loginAttempts;
    }

    public LoginContext login(Shell shell) throws IOException, LoginException {
        TerminalIO io = getTerminalIO();
        assert (io != null);
        io.eraseScreen();
        io.homeCursor();
        io.setBackgroundColor(BLACK);

        String splashPage = getSplashLoader().loadMudSplash();
        io.write(splashPage);
        io.write("");

        LoginContext lc = null;
        try {
            lc = new LoginContext("Mud", this);
        } catch (Exception e) {
            logger.error("Exception caught executing login context", e);
        }

        int logins = 0;
        for (; logins < loginAttempts; logins++) {
            try {
                lc.login();
                break;
            } catch (LoginException e) {
                logger.error("Error occurred logging in with shell " + shell, e);
            }finally {
                if(logins > loginAttempts){
                    terminalIO.writeln("Too many login attempts, sorry!!");
                    throw new LoginException("Too many login attempts");
                }
            }
        }
        return lc;
    }

    /**
     * handles callbacks from the JAAS login module
     * <p/>
     * can handle NameCallback, PasswordCallback, ConfirmationCallback,
     * ActionCallback and TextOutputCallback
     *
     * @param callbacks - a array of callback objects to handle
     *
     */
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (int x = 0; x < callbacks.length; x++) {
            ((SelfHandlingCallback)callbacks[x]).handle(terminalIO, callbackData);
        }
    }

    /**
     * @return the splashLoader
     */
    public SplashLoader getSplashLoader() {
        return splashLoader;
    }


    /**
     * @param terminalIO the terminalIO to set
     */
    public void setTerminalIO(TerminalIO terminalIO) {
        this.terminalIO = terminalIO;
    }

    /**
     * @return the terminalIO
     */
    public TerminalIO getTerminalIO() {
        return terminalIO;
    }
    public Map<String, Object> getCallbackData() {
        return callbackData;
    }
}
